package com.bieber.server.netty.handler;

import com.bieber.common.Constants;
import com.bieber.server.config.ServerConfig;
import com.bieber.server.pack.FilePackage;
import com.bieber.server.serialize.KryoSerializer;
import com.bieber.server.serialize.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.EncoderException;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.ReferenceCountUtil;

import java.io.UnsupportedEncodingException;

/**
 * Created by bieber on 2015/8/19.
 */
public class Encoder  extends MessageToByteEncoder<FilePackage> {


    private static final Serializer DEFAULT_SERIALIZER = new KryoSerializer();
    
    
    private ServerConfig config;

    public Encoder(ServerConfig config) {
        this.config = config;
    }

    /**
     * 整个协议包含消息头和消息体两部分
     * |FRAME_LENGTH(4byte)|HEAD_LENGTH(4byte)|MESSAGE_TYPE(1byte)|FILE_SIZE(8byte)|OFFSET(8byte)|FILENAME_LENGTH(4byte)|FILE_NAME|NODE_NAME_LENGTH(4byte)|NODE_NAME
     * @param ctx
     * @param msg
     * @param out
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext ctx, FilePackage msg, ByteBuf out) throws Exception {
        if(msg instanceof FilePackage){
            FilePackage filePackage = (FilePackage) msg;
            switch (filePackage.getStatus()){
                case START_WRITE:{
                    encodePackage(filePackage,new byte[0],out);
                    break;
                }
                case WRITE_CONTENT:{
                    encodePackage(filePackage, filePackage.getContent().array(), out);
                    break;
                }
                case END_WRITE:{
                    encodePackage(filePackage, new byte[0], out);
                    break;
                }
                case IGNORE:{
                    encodePackage(filePackage, new byte[0], out);
                    break;
                }
                case ERROR:{
                    encodePackage(filePackage,DEFAULT_SERIALIZER.serialize(filePackage.getException()),out);
                    break;
                }
                default:{
                    break;
                }
            }
        }else{
            ctx.write(msg);
        }
    }



    private void encodePackage(FilePackage filePackage,byte[] content,ByteBuf out) throws UnsupportedEncodingException {
        Constants.COMMON_LOGGER.info("send filepackage to remote server package type [{}]",filePackage.getStatus());
        //set head
        // |TOTAL_LENGTH(4byte)|HEAD_LENGTH(4byte)|MESSAGE_TYPE(1byte)|FILE_SIZE(8byte)|OFFSET(8byte)|FILENAME_LENGTH(4byte)|NODE_NAME_LENGTH(4byte)|
        int headLength=4+4+1+8+8+4+4;
        byte[] fileName = filePackage.getFileName().getBytes(config.getCharSet());
        byte[] nodeName = filePackage.getNodeName().getBytes(config.getCharSet());
        int fileNameLength=fileName.length;
        int nodeNameLength=nodeName.length;
        headLength+=fileNameLength+nodeNameLength;
        int length=headLength+content.length;
        out.writeInt(length);//total length
        out.writeInt(headLength);//HEAD_LENGTH(4byte)
        out.writeByte(filePackage.getStatus().getStatus());//MESSAGE_TYPE(1byte)
        out.writeLong(filePackage.getFileSize());//FILE_SIZE(8byte)
        out.writeLong(filePackage.getOffset());//OFFSET(8byte)
        out.writeInt(fileNameLength);//FILENAME_LENGTH(4byte)
        out.writeBytes(fileName);//fileName
        out.writeInt(nodeNameLength);//NODE_NAME_LENGTH(4byte)
        out.writeBytes(nodeName);//nodeName

        //set body
        out.writeBytes(content);

    }

}
